package com.inspur.edp.common.expr.parser;

import com.inspur.edp.common.expr.ast.ASTContext;
import com.inspur.edp.common.expr.ast.CompileUnitParser;
import com.inspur.edp.common.expr.exception.ExpressException;
import com.inspur.edp.common.type.*;
import com.inspur.edp.common.type.cache.TypeThreadCache;
import com.inspur.edp.common.type.utils.TypeUtil;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.*;

/**
 * @author lizhaorui
 * @date 2025/8/18
 * @description
 */
public class DSLTypeBuilder implements ASTContext {

    private Map<String, String> importTypeMap;

    private List<String> genericTypeNames;

    public Type build(String url, String dsl) {
        CompileUnitParser.CompileUnitContext context = ASTParser.parse(dsl).compileUnit();
        DSLTypeBuilder builder = new DSLTypeBuilder();
        return builder.build(url, context).get(0);
    }

    public List<Type> build(String url, CompileUnitParser.CompileUnitContext context) {
        this.importTypeMap = getImportTypeMap(context);
        List<CompileUnitParser.CompileUnitTypesContext> typesContexts = context.compileUnitTypes();
        List<Type> dslClasses = new ArrayList<>();
        for (CompileUnitParser.CompileUnitTypesContext typesContext : typesContexts) {
            Type dslType = new Type();
            //泛型处理
            List<String> genericTypeNameList = getGenericNames(typesContext);
            this.genericTypeNames = genericTypeNameList;
            dslType.setGenericTypeNames(genericTypeNameList);
            dslType.setCode(TypeUtil.getTypeCode(url));
            dslType.setTypeKind(this.getTypeKind(typesContext));
            List<Field> fieldMds = getFields(typesContext);
            //枚举值|字段设置
            List<Field> enumFields = this.getEnumValues(typesContext, url);
            fieldMds.addAll(enumFields);
            dslType.setFields(fieldMds);
            List<Method> methodMds = getMethods(typesContext);
            dslType.setMethods(methodMds);

            Map<String, DecorationOption> decorationOptionMap = DecoratorParser.getDecoratorMap(typesContext.typeDeclaration().decorator());
            dslType.setDecorationMap(decorationOptionMap);

            //document
            Document document = this.getDocument(decorationOptionMap);
            if (document != null) {
                dslType.setName(document.getName());
            }

            //extends 处理
            CompileUnitParser.ExtendDeclarationContext extendDeclarationContext = typesContext.typeDeclaration().extendDeclaration();
            if (extendDeclarationContext != null) {
                CompileUnitParser.TypeContext typeContext = extendDeclarationContext.type();
                TypeRefer extendType = TypeAstParser.getTypeRefer(typeContext, this);
                dslType.setExtendType(extendType);
            }

            //implements 处理
//            CompileUnitParser.ImplementsDeclarationContext implementsDeclarationContext = typesContext.typeDeclaration().implementsDeclaration();
//            if (implementsDeclarationContext != null) {
//                List<String> implementList = new ArrayList<>();
//                for (CompileUnitParser.TypeContext typeContext : implementsDeclarationContext.type()) {
//                    String implementType = TypeAstParser.getTypeFullName(typeContext, this);
//                    implementList.add(implementType);
//                }
//                dslType.setImplementList(implementList);
//            }
            this.genericTypeNames = null;
            dslClasses.add(dslType);
        }
        return dslClasses;
    }

    private Map<String, String> getImportTypeMap(CompileUnitParser.CompileUnitContext context) {
        //类型名字映射
        Map<String, String> importMap = new HashMap<>();
        //导入类型映射
        List<CompileUnitParser.ImportInfoContext> imports = context.importInfo();
        for (CompileUnitParser.ImportInfoContext importInfo : imports) {
            String from = importInfo.StringLiteral().getText();
            for (TerminalNode simpleName : importInfo.SimpleName()) {
                String typeSimpleName = simpleName.getText();
                importMap.put(typeSimpleName, from.substring(1, from.length() - 1));
            }
        }
        return importMap;
    }

    private TypeKind getTypeKind(CompileUnitParser.CompileUnitTypesContext typesContext) {
        CompileUnitParser.TypeDeclarationContext typeDeclaration = typesContext.typeDeclaration();
        if (typeDeclaration.Class() != null) {
            return TypeKind.CLASS;
        } else if (typeDeclaration.Enum() != null) {
            return TypeKind.ENUM;
        } else if (typeDeclaration.Interface() != null) {
            return TypeKind.INTERFACE;
        } else {
            return TypeKind.CLASS;
        }
    }

    private List<Field> getEnumValues(CompileUnitParser.CompileUnitTypesContext typesContext, String url) {
        List<Field> enumValues = new ArrayList<>();
        CompileUnitParser.TypeDeclarationContext typeDeclaration = typesContext.typeDeclaration();
        CompileUnitParser.BodyDeclarationsContext bodyDeclarationsContext = typeDeclaration.bodyDeclarations();
        if (bodyDeclarationsContext != null) {
            CompileUnitParser.EnumValuesContext enumValuesContext = bodyDeclarationsContext.enumValues();
            if (enumValuesContext != null) {
                List<CompileUnitParser.EnumValueContext> enumValueContexts = enumValuesContext.enumValue();
                if (enumValueContexts != null) {
                    for (CompileUnitParser.EnumValueContext enumValueContext : enumValueContexts) {
                        String enumKey = enumValueContext.SimpleName().getText();
                        // String enumValue = enumValueContext.express().getText();
                        Field dslField = new Field();
                        dslField.setStatic(true);
                        dslField.setCode(enumKey);

                        dslField.setType(TypeThreadCache.getByUrl(url).buildTypeRefer());
                        enumValues.add(dslField);
                    }
                }
            }
        }
        return enumValues;
    }


    private List<Field> getFields(CompileUnitParser.CompileUnitTypesContext typesContext) {
        CompileUnitParser.TypeDeclarationContext typeDeclaration = typesContext.typeDeclaration();
        List<Field> dslFields = new ArrayList<>();
        if (typeDeclaration.bodyDeclarations() != null) {
            List<CompileUnitParser.FieldDeclarationContext> fieldDeclarationContexts = typeDeclaration.bodyDeclarations().fieldDeclaration();
            if (fieldDeclarationContexts != null) {
                for (CompileUnitParser.FieldDeclarationContext field : fieldDeclarationContexts) {
                    String fieldCode = field.SimpleName().getText();
                    TypeRefer typeRefer = TypeAstParser.getTypeRefer(field.type(), this);
                    TerminalNode staticNode = field.Static();
                    Field dslField = new Field();
                    if (staticNode != null) {
                        dslField.setStatic(true);
                    } else {
                        dslField.setStatic(false);
                    }
                    dslField.setCode(fieldCode);
                    dslField.setType(typeRefer);
                    //装饰器 FieldDesc
                    Map<String, DecorationOption> decorationOptionMap = DecoratorParser.getDecoratorMap(field.decorator());
                    dslField.setDecorationMap(decorationOptionMap);
                    Document document = this.getDocument(decorationOptionMap);
                    if (document != null) {
                        dslField.setName(document.getName());
                    }
                    dslFields.add(dslField);
                }
            }
        }
        return dslFields;
    }

    private List<Method> getMethods(CompileUnitParser.CompileUnitTypesContext typesContext) {
        CompileUnitParser.TypeDeclarationContext typeDeclaration = typesContext.typeDeclaration();
        List<Method> methodMds = new ArrayList<>();
        if (typeDeclaration.bodyDeclarations() != null) {
            List<CompileUnitParser.MethodDeclarationContext> methodDeclarationContexts = typeDeclaration.bodyDeclarations().methodDeclaration();
            if (methodDeclarationContexts != null) {
                for (CompileUnitParser.MethodDeclarationContext methodContext : methodDeclarationContexts) {
                    Method methodMd = new Method();

                    //方法注解
                    Map<String, DecorationOption> decorationOptionMap = DecoratorParser.getDecoratorMap(methodContext.decorator());
                    methodMd.setDecorationMap(decorationOptionMap);
                    Document document = this.getDocument(methodMd.getDecorationMap());
                    if (document != null) {
                        methodMd.setName(document.getName());
                    }
                    TerminalNode staticNode = methodContext.Static();
                    if (staticNode != null) {
                        methodMd.setStatic(true);
                    } else {
                        methodMd.setStatic(false);
                    }
                    String methodName = methodContext.SimpleName().getText();
                    TypeRefer returnType = TypeAstParser.getTypeRefer(methodContext.type(), this);
                    methodMd.setCode(methodName);
                    methodMd.setReturnType(returnType);
                    List<Parameter> parameters = new ArrayList<>();
                    if (methodContext.parameterDeclarations() != null) {
                        List<CompileUnitParser.ParameterDeclarationContext> pContexts = methodContext.parameterDeclarations().parameterDeclaration();
                        if (pContexts != null) {
                            for (CompileUnitParser.ParameterDeclarationContext pContext : pContexts) {
                                Parameter parameterMd = new Parameter();
                                String paramName = pContext.SimpleName().getText();
                                parameterMd.setCode(paramName);
                                Map<String, DecorationOption> paramDecoratorMaps = DecoratorParser.getDecoratorMap(pContext.decorator());
                                parameterMd.setOptions(paramDecoratorMaps);
                                Document document1 = this.getDocument(paramDecoratorMaps);
                                if (document1 != null) {
                                    parameterMd.setName(document1.getName());
                                }
                                CompileUnitParser.TypeContext paramTypeContext = null;
                                if (pContext.VarArgs() != null) {
                                    parameterMd.setVarArgs(true);
                                    CompileUnitParser.ArrayTypeContext arrayTypeContext = (CompileUnitParser.ArrayTypeContext) pContext.type();
                                    paramTypeContext = arrayTypeContext.type();
                                } else {
                                    paramTypeContext = pContext.type();
                                }
                                TypeRefer paramType = TypeAstParser.getTypeRefer(paramTypeContext, this);
                                parameterMd.setType(paramType);
                                parameters.add(parameterMd);
                            }
                        }
                    }
                    methodMd.setParameters(parameters);
                    methodMds.add(methodMd);
                }
            }
        }
        return methodMds;
    }

    private Document getDocument(Map<String, DecorationOption> optionMap) {
        if (optionMap == null) {
            return null;
        }
        DecorationOption decorationOption = optionMap.get(DecorationOption.DOCUMENT_NAME);
        if (decorationOption == null) {
            return null;
        } else {
            return new Document(decorationOption.getOptionMap());
        }
    }

    private List<String> getGenericNames(CompileUnitParser.CompileUnitTypesContext typesContext) {
        List<String> genericNames = null;
        CompileUnitParser.TypeDeclarationContext typeDeclaration = typesContext.typeDeclaration();
        CompileUnitParser.GenericTypeDeclarationContext genericTypeDeclarationContext = typeDeclaration.genericTypeDeclaration();
        if (genericTypeDeclarationContext != null) {
            List<TerminalNode> simpleNames = genericTypeDeclarationContext.SimpleName();
            genericNames = new ArrayList<>();
            for (TerminalNode node : simpleNames) {
                genericNames.add(node.getText());
            }
        }
        return genericNames;
    }


    @Override
    public Type getThisType() {
        throw new ExpressException("not implement.");
    }

    @Override
    public Type getType(TypeRefer typeRefer) {
        throw new ExpressException("not implement.");
    }

    @Override
    public Type getType(String url) {
        throw new ExpressException("not implement.");
    }

    @Override
    public void addImport(String fullName) {

    }

    @Override
    public void addImports(Collection<String> imports) {

    }

    @Override
    public boolean containImportName(String typeName) {
        return this.importTypeMap.containsKey(typeName);
    }

    @Override
    public String getImportFullName(String typeName) {
        return this.importTypeMap.get(typeName);
    }

    @Override
    public void addVariable(String varName, TypeRefer typeRefer) {

    }

    @Override
    public void removeVariable(String varName) {

    }

    @Override
    public TypeRefer getVarType(String varName) {
        throw new ExpressException("not implement.");
    }

    @Override
    public boolean containVarName(String varName) {
        throw new ExpressException("not implement.");
    }


    @Override
    public List<String> getGenericTypeNames() {
        if (this.genericTypeNames == null) {
            this.genericTypeNames = new ArrayList<>();
        }
        return genericTypeNames;
    }


}
